package com.google.code.avajhelpdesk.persistence;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.LockMode;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository(value="genericDAO")
public class GenericHibernateDAO extends HibernateDaoSupport implements IGenericDAO {
	
//	private final Logger logger = LoggerFactory.getLogger(GenericHibernateDAO.class);
	
	/* (non-Javadoc)
	 * @see ru.ifmo.support.persistence.IGenericDAO#getInstance(java.lang.Class, java.io.Serializable)
	 */
	@Transactional
	public <T> T get(Class<T> type, Serializable id) {
		@SuppressWarnings("unchecked") 
		T result = (T) getHibernateTemplate().load(type, id);
		return result;
	}

	/* (non-Javadoc)
	 * @see ru.ifmo.support.persistence.IGenericDAO#getAllInstances(java.lang.Class)
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public <T> List<T> findAll(Class<T> type) {
		List result = findByCriteria(DetachedCriteria.forClass(type));
		return Collections.checkedList(result, type);
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public <T> List<T> findAll(Class<T> type, int firstToRetrive, int maxToRetrive) {
		List result = findByCriteria(DetachedCriteria.forClass(type), firstToRetrive, maxToRetrive);
		return Collections.checkedList(result, type);
	}

	
	/* (non-Javadoc)
	 * @see ru.ifmo.support.persistence.IGenericDAO#persist(T)
	 */
	@Transactional
	public <T> T persist(T entity) {
		getHibernateTemplate().persist(entity);
		return entity;
	}
	
	/* (non-Javadoc)
	 * @see ru.ifmo.support.persistence.IGenericDAO#merge(T)
	 */
	@Transactional
	public <T> T merge(T entity) {
		getHibernateTemplate().merge(entity);
		return entity;
	}
	
	/* (non-Javadoc)
	 * @see ru.ifmo.support.persistence.IGenericDAO#remove(java.lang.Object)
	 */
	@Transactional
	public void remove(Object entity) {
		getHibernateTemplate().delete(entity);
	}

	/* (non-Javadoc)
	 * @see ru.ifmo.support.persistence.IGenericDAO#findByCriteria(org.hibernate.criterion.DetachedCriteria, int, int)
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public List findByCriteria(DetachedCriteria criteria, int firstToRetrive, int maxToRetrive) {
		return getHibernateTemplate().findByCriteria(criteria, firstToRetrive, maxToRetrive);
	}

	/* (non-Javadoc)
	 * @see ru.ifmo.support.persistence.IGenericDAO#findByCriteria()
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public List findByCriteria(DetachedCriteria criteria) {
		return findByCriteria(criteria, 0, 0);
	}

	/* (non-Javadoc)
	 * @see ru.ifmo.support.persistence.IGenericDAO#getAllTypes()
	 */
	@SuppressWarnings("unchecked")
	public List<Class> getAllTypes() {
		List<Class> result = new ArrayList<Class>();
		for (Object o : getSessionFactory().getAllClassMetadata().values()) {
			ClassMetadata classMetadata = (ClassMetadata) o;
			result.add(classMetadata.getMappedClass(EntityMode.POJO));
		}
		return result;
	}
	
	/* (non-Javadoc)
	 * @see ru.ifmo.support.persistence.IGenericDAO#reattach(java.lang.Object)
	 */
	@Transactional
	public void reattach(Object entity) {
		getSession().lock(entity, LockMode.NONE);
	}

	@Transactional
	public <T> int count(Class<T> type) {
		Criteria query = getSession()
					.createCriteria(type)
						.setProjection(Projections.rowCount());
		
		return ((Integer) query.uniqueResult()).intValue();
	}
}
